package com.ep.thirdPlatformProject.system.util;


import cn.hutool.core.lang.Opt;
import com.ep.thirdPlatformProject.system.config.minio.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.apache.tika.mime.MimeTypes;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
@RequiredArgsConstructor
public class MinioUtil {

    private final MinioClient minioClient;

    private final MinioConfig configuration;

    /**
     * 判断bucket是否存在，不存在则创建
     */
    public boolean existBucket(String bucketName) {
        boolean exists;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                log.info("创建bucketName = {}完成!", bucketName);
                exists = true;
            }
            log.info("bucketName = {}已存在！策略为：{}", bucketName, minioClient.getBucketPolicy(
                    GetBucketPolicyArgs.builder().bucket(bucketName).build())
            );
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
            exists = false;
        }
        return exists;
    }

    /**
     * 删除bucket
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * 上传文件
     *
     * @param file     文件
     * @param fileName 文件名称
     */
    public void upload(MultipartFile file, String fileName) {
        // 使用putObject上传一个文件到存储桶中。
        try {
            Opt.ofNullable(file).orElseThrow(() -> new RuntimeException("上传文件不存在，请仔细检查"));

            InputStream inputStream = file.getInputStream();
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
        }
    }

    /**
     * 上传
     *
     * @param file     文件
     * @param fileName 文件名称
     * @return {@link String } 上传文件的链接
     */
    public String upload(File file, String fileName) {
        // 使用putObject上传一个文件到存储桶中。
        try {
            // 判断文件是否存在
            Opt.ofNullable(file).orElseThrow(() -> new RuntimeException("上传文件不存在，请仔细检查"));
            // 判断bucket是否存在
            boolean isExist = this.existBucket(configuration.getBucketName());

            // 上传文件使用雪花算法的id，tika计算出文件的属性
            String contentType = new Tika().detect(file);
            String extension = MimeTypes.getDefaultMimeTypes().forName(contentType).getExtension();
            InputStream inputStream = new FileInputStream(file);
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(configuration.getBucketName())
                    .object(fileName + extension)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(contentType)
                    .build());
            // 需要设置为public
            return fileName + extension;
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
            throw new RuntimeException("minio使用错误：" + e.getMessage(), e);
        }
    }

    /**
     *  url 上传
     *
     * @param urlStr        urlStr
     * @param bucketName bucket名称
     * @param fileName   文件名称
     * @return {@link String }
     */
    public String upload(String urlStr ,String bucketName, String fileName) {
        // 使用putObject上传一个文件到存储桶中。
        try {
            // 此处获取到流并且计算出长度
            InputStream inputStream = FileUtil.returnByteStream(urlStr);

//            // 判断bucket是否存在
            boolean isExist = this.existBucket(bucketName);
            String contentType = new Tika().detect(inputStream);
            String extension = MimeTypes.getDefaultMimeTypes().forName(contentType).getExtension();

            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName + extension)
                    .stream(inputStream, inputStream.available(), -1)
                    .contentType(contentType)
                    .build());
            // 需要设置为public
            return fileName + extension;
        } catch (Exception e) {
            log.error("minio上传文件错误：{}", e.getMessage(), e);
            throw new RuntimeException("minio上传文件错误：" + e.getMessage(), e);
        }
    }
    /**
     * 获取文件访问地址（有过期时间）
     *
     * @param fileName 文件名称
     * @param time     时间
     * @param timeUnit 时间单位
     */
    public String getExpireFileUrl(String fileName, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(fileName)
                    .expiry(time, timeUnit)
                    .build()
            );
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
        }
        return null;
    }

    /**
     * 获取文件签名后的下载地址
     *
     * @param fileName 文件名称
     */
    public String getSignFileUrl(String fileName, Boolean download) {
        try {
            GetPresignedObjectUrlArgs.Builder builder = GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(configuration.getBucketName())
                    .object(fileName);
            if (download) {
                Map<String, String> headers = new HashMap<>();
                headers.put("response-content-type", "application/octet-stream");
                builder.extraQueryParams(headers);
            }
            GetPresignedObjectUrlArgs args = builder.build();
            return minioClient.getPresignedObjectUrl(args);
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
        }
        return null;
    }

    /**
     * 下载文件
     *
     * @param fileName 文件名称
     */
    public void download(HttpServletResponse response, String fileName) {
        InputStream in = null;
        try {
            // 获取对象信息
            StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            // 文件下载
            in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
            IOUtils.copy(in, response.getOutputStream());
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    log.error("minio使用错误：{}", e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 删除文件
     *
     * @param fileName 文件名称
     */
    public void delete(String fileName) {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
        } catch (Exception e) {
            log.error("minio使用错误：{}", e.getMessage(), e);
        }
    }


    /**
     * 获取预览url
     *
     * @param fileName 文件名称
     * @return {@link String }
     */
    public String getPreviewUrl(String fileName) {

        return configuration.getUrl() + "/" + configuration.getBucketName() + "/" + fileName;
    }
}